'''
Created on Aug 13, 2012

@author: petrina
'''

import logging
import datetime
from interval_list import Interval_list
from agg_record import Agg_record
from error.data_exceptions import Record_exception

class Record_list(object):
    '''
    Record_list provides a container of agg_record-objects.
    
    It works like a dict, in order to provide a sorted list for each interval. 
    The interval-number is the key. The list of records for each interval is the value.
    This value contains a Interval_list, which is a container for fleet-data of a specific time interval.
    Moreover there is a method in order to generalize or aggregate the list, 
    so that a day has a lower amount of bigger intervals.  
    '''
    
    
    def __init__(self, name = '', intervals = 96):
        '''        
        @param name: a name for the list (for exporting the list)
        @param intevals: number of time intervals on one day 
        '''
        
        # the internal structure is a dict
        self._recorddict = dict()
        
        # for every interval create a dict key, with an empty list as value
        # these empty lists are later (usually during a DB-import) filled with records of this time interval
        for i in range(intervals):
            self._recorddict[i] = Interval_list(i)
        
        self._intervals = intervals 
        self.name = name
        
        logging.debug("Record_list: created new Record_list")


    #-----------------------getter- and setter-methods-------------------------
    
    def get_intervals(self):
        return self._intervals
    
    def get_is_kph(self):
        
        # find the first occurance of is_kph in the data
        for interval in self._recorddict:
            for agg_record in self._recorddict[interval]:
                if agg_record is not None:
                    return agg_record.is_kph
        
        return None


    def set_intervals(self, value):
        self._intervals = value


    def del_intervals(self):
        del self._intervals


    def get_name(self):
        return self.__name


    def get_begintime(self):
        '''
        gets the earliest timestamp by searching the datastructure
        
        @return: timestamp, if there is no data at all it returns None
        '''
        
        if len(self._recorddict) > 0:
            contains_data = False
            for i in self._recorddict:
                if  len(self._recorddict[i]) > 0:
                    contains_data = True
                    break
                
            if contains_data:    
                time = datetime.datetime.now()
                
                # search for the oldest timestamp
                for interval in self._recorddict:
                    min_time = self._recorddict[interval].begintime
                    if min_time is not None and min_time < time:
                        time = min_time
                return time
            else:
                return None
        
        else:
            return None


    def get_endtime(self):
        '''
        gets the most recent timestamp by searching the datastructure
        
        @return: timestamp, if there is no data at all it returns None
        '''
        
        if len(self._recorddict) > 0:
            
            # is there any data in the datastructure
            contains_data = False
            for i in self._recorddict:
                if  len(self._recorddict[i]) > 0:
                    contains_data = True
                    break
                
            if contains_data:    
                init_time = datetime.datetime(1900,1,1)
                time = init_time
                
                # search for the oldest timestamp
                for interval in self._recorddict:
                    max_time = self._recorddict[interval].endtime
                    if max_time is not None and max_time > time:
                        time = max_time
                if time == init_time:
                    return None
                else:
                    return time
            else:
                return None
        
        else:
            return None


   
    def set_name(self, value):
        self.__name = value


    def del_name(self):
        del self.__name
        
        
    #----------------------public methods of the class-------------------------
    
    def __iter__(self):
        return self._recorddict.__iter__()
    
    
    def next(self):
        pass
    
    
    def __getitem__(self, key):
        return self._recorddict[key]
    
    
    def items(self):
        return self._recorddict.items()
    
    
    def __len__(self):
        return len(self._recorddict)
   
    
    def plot(self, plotobject, attribute = "speed", name='data', exp_name ='data',color = 'blue'):
        '''
        plots the data in the Record_list and exports the figure to png.
        
        @param plotobject: object for plotting (such as Plot-instance)
        @param attribute: attribute-value for the y-value, such as traveltime or speed (default: "speed")
        @param name: name for the data in the title (default: 'data')
        @param exp_name:    name for the export 
        @param color: color of the plotted points   
        '''
        
        logging.debug("Record_list: trying to plot data in the Record_list and exporting")
        
        unit = ""
        if attribute == "speed":
            unit = "km/h" if self.is_kph else "m/s"
        elif attribute == "traveltime":
            unit = "s"
        
        plotobject.create_new_plot(title = 'Data: ' + name, xlabel='time interval', 
                                   ylabel= attribute + ' ['+unit+']')        
        
        for i in self._recorddict:  
            self._recorddict[i].plot(plotobject, attribute, color)
        
        # export plot
        plotobject.save_plot2png(exp_name, 'png')    
              
    
    
    def add_record(self, record):
        '''
        This method adds a Link-record-object to the record_list. 
        
        Therefore the record shall be of the type agg_record.
        The record-object must have an interval-attribute in order 
        to organize the given data in the list.
        '''
        
        try:
            # get existing data-list from the internal datastructure and add the record to that list
            self._recorddict[record.interval].add_record(record)
            
            logging.debug("Record_list: new record was added to the interval sorted Record_list")
        except:
            # obviously no record-instance
            logging.error("Record_list: cannot add the given object to the Record_list " + str(record))
            raise Record_exception("cannot add the given object to the Record_list")
    
                    

    def aggregated_List(self, min_interval, link = None):
        '''
        if it is possible the method returns a new Record_list, which aggregates to the data to the given interval in minutes
        
        The new time interval size must integrate well with the old one, so that all
        time intervals are filled equally (e.g. the old interval is 15 minutes means that
        30 minutes, 1 hour or 2 hours would work well, however 20 minutes are not possible yet).
        The new time_interval must be bigger than the old one.
        
        @param min_interval: new time interval size in minutes  
        '''
        nb_int = int(24 * 60 / min_interval)
        logging.info("Record_list: trying to aggregate the data with new timeinterval of " \
                     + str(min_interval) + " minutes (i.e. " + str(nb_int) + " intervals for one day")
        # new number of intervals
        
        if self._intervals == nb_int:
            logging.error("Record_list: invalid aggregation because new and old time interval are equal " + str(min_interval) + " minutes")
            return self
        
        if self._intervals % nb_int == 0:
            recorddict = Record_list(intervals = nb_int)
            count = self._intervals / nb_int
            
            min_time = self.begintime
            max_time = self.endtime
            
            is_kph=self.is_kph
            # iterate over intervals (already grouped)
            
            # new interval number
            newint = 0
            for i in range(0,len(self._recorddict), count):
                date = min_time
                
                
                # for every day
                while date <= max_time:  
                    sum_count = 0
                    agg_cov = 0
                    agg_v = 0
                    std = 0
                    
                    hour = newint * min_interval/60
                    min = newint * min_interval%60
                    timestamp = datetime.datetime(date.year, date.month, date.day, hour, min, 00)
                    nb_val = 0
                    
                    # aggregate for every interval on this day      
                    for j in range(i, i+count):
                        rec = self._recorddict[j].search_data(date)
                        
                        if rec is not None:
                            sum_count += rec.count
                            std += rec.std
                            agg_cov += rec.coverage
                            agg_v += 1/rec.speed
                            nb_val += 1

                    # average/harmonic values
                    if agg_v > 0:
                        agg_v = nb_val/agg_v
                    
                    if nb_val > 0:
                        std = std/nb_val
                            
                        # fill the new Record_list
                        newrec = Agg_record(agg_v, agg_cov, sum_count, 'aggregated', timestamp, 
                                            link, int_size = min_interval, std = std, is_kph=is_kph)
                        recorddict.add_record(newrec)
                    
                    # go to the next day
                    date += datetime.timedelta(days=1)
                
                # increase the time interval
                newint += 1
            
            return recorddict
    
        else: 
            logging.error("Record_list: invalid timeinterval of " + str(min_interval) + " minutes -- does not work with existing time intervals")
            return None
            
            
        
    #--------------assign properties using get and set-methods-----------------
    name = property(get_name, set_name, del_name, "name or description of the list")
    begintime = property(get_begintime, None, None, "timestamp of the earliest record (calculated from input data)")
    endtime = property(get_endtime, None, None, "timestamp of the most up-to-date-record (calculated from input data)")
    intervals = property(get_intervals, set_intervals, del_intervals, "number of intervals on one day (e.g. 96 if there are 15-minutes-intervals)")
    is_kph = property(get_is_kph)
